package com.jtny.nytb.equipment.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jtny.component.core.util.PageUtils;
import com.jtny.component.entity.PageData;
import com.jtny.component.entity.PageParam;
import com.jtny.component.exception.ServiceException;
import com.jtny.nytb.equipment.dao.araetype.DeviceAreaDAO;
import com.jtny.nytb.equipment.domain.areatype.DeviceAreaDO;
import com.jtny.nytb.equipment.service.DeviceAreaService;
import com.jtny.nytb.equipment.utils.CopyList;
import com.jtny.nytb.equipment.utils.DealPageData;
import com.jtny.nytb.equipment.vo.typeAndArea.DeviceAreaExcelVO;
import com.jtny.nytb.equipment.vo.typeAndArea.DeviceAreaRequestVO;
import com.jtny.nytb.equipment.vo.typeAndArea.DeviceAreaResponseVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DeviceAreaServiceImpl extends ServiceImpl<DeviceAreaDAO, DeviceAreaDO>
        implements DeviceAreaService {
    @Resource
    DeviceAreaDAO deviceAreaDAO;
    @Override
    public DeviceAreaResponseVO getDeviceAreaByName(String name) throws ServiceException {
        String[] split = name.split(StrPool.SLASH);
        String lastName = split[split.length - 1];
        List<DeviceAreaDO> dos = this.baseMapper.selectList(buildWrapper(lastName));
        if (CollUtil.isEmpty(dos)) {
            return null;
        }
        if (dos.size() > 1) {
            // key: name value: id
            Map<String, DeviceAreaDO> tempMap = new HashMap<>();
            dos.forEach(area -> {
                String[] split1 = area.getAllPath().split(StrPool.COMMA);
                List<DeviceAreaDO> tempList = this.baseMapper.selectList(Wrappers.<DeviceAreaDO>lambdaQuery().in(DeviceAreaDO::getId, (Object) split1));
                String namePath = tempList.stream().map(DeviceAreaDO::getName).collect(Collectors.joining(StrPool.SLASH));
                tempMap.put(namePath, area);
            });
            DeviceAreaDO deviceAreaDO = tempMap.get(name);
            DeviceAreaResponseVO toReturn = new DeviceAreaResponseVO();
            BeanUtils.copyProperties(deviceAreaDO, toReturn);
            return toReturn;
        } else {
            DeviceAreaResponseVO toReturn = new DeviceAreaResponseVO();
            BeanUtils.copyProperties(dos.get(0), toReturn);
            return toReturn;
        }
    }
    @Override
    public Map<String, DeviceAreaResponseVO> mapDeviceAreaByNameList(List<String> areaNameList) throws ServiceException {
        Map<String, DeviceAreaResponseVO> map = new HashMap<>();
        areaNameList.forEach(areaName -> map.put(areaName, getDeviceAreaByName(areaName)));
        return map;
    }

    private Wrapper<DeviceAreaDO> buildWrapper(String name) {
        return Wrappers.<DeviceAreaDO>lambdaQuery().eq(DeviceAreaDO::getName, name);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateDeviceArea(List<DeviceAreaRequestVO> vos) {
        if (CollUtil.isNotEmpty(vos)) {
            //校验
            Long id = vos.get(0).getId();
            Set<String> names = vos.stream().map(DeviceAreaRequestVO::getName).collect(Collectors.toSet());
            if (names.size() < vos.size()) {
                throw new ServiceException("名称重复！");
            }

            String msg = checkNameDup(vos, Objects.isNull(id) || id == 0);
            if (StringUtils.isNotEmpty(msg)) {
                throw new ServiceException("名称重复:" + msg);
            }

            //实体类
            List<DeviceAreaDO> deviceAreaDOS = new ArrayList<>();
            if (vos.get(0).getId() == null || vos.get(0).getId() == 0) {
                vos.forEach(e -> {
                    DeviceAreaDO deviceAreaDO = new DeviceAreaDO();
                    //新增
                    String parentCode = "";
                    if (e.getLevel() > 1) {
                        DeviceAreaRequestVO deviceAreaRequestVO = new DeviceAreaRequestVO();
                        deviceAreaRequestVO.setId(e.getParentId());
                        List<DeviceAreaResponseVO> deviceAreaResultVos = selectList(deviceAreaRequestVO);
                        parentCode = CollUtil.isEmpty(deviceAreaResultVos) ? "" : deviceAreaResultVos.get(0).getCode();
                    }

                    // 生成新的唯一编码
                    String newCode;
                    if (StringUtils.isEmpty(parentCode)) {
                        // 如果是一级节点，获取全局最大code并递增
                        String maxCode = deviceAreaDAO.getMaxCodeForLevel(1);
                        int maxCodeInt = StringUtils.isEmpty(maxCode) ? 0 : Integer.parseInt(maxCode);
                        newCode = String.format("%03d", maxCodeInt + 1);
                    } else {
                        // 如果是子节点，获取父节点下的最大code并递增
                        String maxCode = deviceAreaDAO.getMaxCodeForParent(parentCode);
                        int maxCodeInt = StringUtils.isEmpty(maxCode) ? 0 : Integer.parseInt(maxCode.substring(parentCode.length()));
                        newCode = parentCode + String.format("%03d", maxCodeInt + 1);
                    }

                    e.setCode(newCode);

                    BeanUtils.copyProperties(e, deviceAreaDO);
                    deviceAreaDOS.add(deviceAreaDO);
                });
            }

            if (id == null || id == 0) {
                saveBatch(deviceAreaDOS, 500);
                //批量更新
                deviceAreaDOS.forEach(e -> {
                    String path = StringUtils.isEmpty(e.getAllPath()) ? "0" : e.getAllPath();
                    e.setAllPath(path + "," + e.getId());
                });
                updateBatchById(deviceAreaDOS);
            } else {
                updateBatchById(deviceAreaDOS, 500);
            }
        }
    }

    // ExcelVo转换为RequestVo
    private List<DeviceAreaRequestVO> convertToRequestVO(List<DeviceAreaExcelVO> excelVOs) {
        return excelVOs.stream()
                .map(excelVO -> {
                    DeviceAreaRequestVO requestVO = new DeviceAreaRequestVO();
                    requestVO.setName(excelVO.getName());
                    requestVO.setCode(excelVO.getCode());
                    requestVO.setParentName(excelVO.getParentName());
                    requestVO.setLevel(excelVO.getLevel());
                    return requestVO;
                })
                .collect(Collectors.toList());
    }


    public String checkNameDup(List<DeviceAreaRequestVO> deviceAreaRequestVOS, Boolean isAdd) {
        if (CollUtil.isEmpty(deviceAreaRequestVOS)) {
            return "";
        }
        Set<String> names = deviceAreaRequestVOS.stream().map(DeviceAreaRequestVO::getName).collect(Collectors.toSet());

        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = new QueryWrapper<>();
        StringBuilder error = new StringBuilder();
        if (Boolean.TRUE.equals(isAdd)) {
            deviceAreaDOQueryWrapper.lambda().in(DeviceAreaDO::getName, names);
            List<DeviceAreaDO> list = list(deviceAreaDOQueryWrapper);
            if (CollUtil.isNotEmpty(list)) {
                list.forEach(e -> error.append(e.getName()).append(";"));
            }
        } else {
            deviceAreaRequestVOS.forEach(e -> {
                deviceAreaDOQueryWrapper.clear();
                deviceAreaDOQueryWrapper.lambda().ne(DeviceAreaDO::getId, e.getId())
                        .eq(DeviceAreaDO::getName, e.getName());
                List<DeviceAreaDO> list = list(deviceAreaDOQueryWrapper);
                if (CollUtil.isNotEmpty(list)) {
                    error.append(list.get(0).getName()).append(";");
                }
            });
        }
        if (StringUtils.isNotEmpty(error.toString())) {
            return "名称重复:" + error;
        }

        return error.toString();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void enableArea(DeviceAreaRequestVO deviceAreaRequestVO) {
        UpdateWrapper<DeviceAreaDO> deviceAreaUpdateWrapper = new UpdateWrapper<>();
        deviceAreaUpdateWrapper.lambda().set(DeviceAreaDO::getStatus, deviceAreaRequestVO.getStatus())
                .eq(DeviceAreaDO::getId, deviceAreaRequestVO.getId());
        update(deviceAreaUpdateWrapper);
    }

    @Override
    public List<DeviceAreaResponseVO> selectTree() {
        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = new QueryWrapper<>();
        deviceAreaDOQueryWrapper.lambda()
                .select(DeviceAreaDO::getAllPath, DeviceAreaDO::getCode,
                        DeviceAreaDO::getName,
                        DeviceAreaDO::getLevel, DeviceAreaDO::getParentId, DeviceAreaDO::getId);
        List<DeviceAreaDO> list = list(deviceAreaDOQueryWrapper);
        List<DeviceAreaResponseVO> deviceAreaResponseVOS = new ArrayList<>();
        this.copyVO(list, deviceAreaResponseVOS);
        return deviceAreaResponseVOS;
    }
    @Override
    public List<DeviceAreaResponseVO> selectListForRecu(DeviceAreaRequestVO deviceAreaRequestVO) {
        List<DeviceAreaResponseVO> deviceAreaResponseVOS = selectList(deviceAreaRequestVO);
        if (CollUtil.isEmpty(deviceAreaResponseVOS)) {
            return new ArrayList<>(0);
        }
        Integer level = deviceAreaResponseVOS.stream().min(Comparator.comparing(DeviceAreaResponseVO::getLevel)).get().getLevel();
        deviceAreaResponseVOS.stream().filter(s -> s.getLevel().equals(level)).forEach(parent -> getSonList(deviceAreaResponseVOS, parent.getId()));
        return deviceAreaResponseVOS.stream().filter(s -> s.getLevel().equals(level)).collect(Collectors.toList());
    }

    public void getSonList(List<DeviceAreaResponseVO> deviceAreaResponseVOS, Long parentId) {
        List<DeviceAreaResponseVO> collect =
                deviceAreaResponseVOS.stream().filter(e -> e.getParentId().equals(parentId)).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(collect)) {
            deviceAreaResponseVOS.stream().filter(s -> s.getId().equals(parentId)).forEach(sum -> sum.setChildren(collect));
            collect.forEach(son -> getSonList(deviceAreaResponseVOS, son.getId()));
        }
    }

    @Override
    public List<DeviceAreaResponseVO> selectList(DeviceAreaRequestVO deviceAreaRequestVO) {
        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = getDeviceAreaDOQueryWrapper(deviceAreaRequestVO);
        List<DeviceAreaDO> list = list(deviceAreaDOQueryWrapper);
        List<DeviceAreaResponseVO> deviceAreaResponseVOS = CopyList.copyList(list, DeviceAreaResponseVO.class);
        if (CollUtil.isNotEmpty(deviceAreaResponseVOS)) {
            getParentName(deviceAreaResponseVOS);
        }
        return deviceAreaResponseVOS;
    }

    private void getParentName(List<DeviceAreaResponseVO> deviceAreaResponseVOS) {
        //父id
        Set<Long> parIds = deviceAreaResponseVOS.stream().map(DeviceAreaResponseVO::getParentId).collect(Collectors.toSet());
        QueryWrapper<DeviceAreaDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(DeviceAreaDO::getId, parIds);
        Map<Long, String> collectById = list(wrapper).stream().collect(Collectors.toMap(DeviceAreaDO::getId, DeviceAreaDO::getName));
        deviceAreaResponseVOS.forEach(e -> e.setParentName(collectById.get(e.getParentId())));
    }

    @NotNull
    private QueryWrapper<DeviceAreaDO> getDeviceAreaDOQueryWrapper(DeviceAreaRequestVO deviceAreaRequestVO) {
        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = new QueryWrapper<>();
        deviceAreaDOQueryWrapper.lambda()
                .eq(Objects.nonNull(deviceAreaRequestVO.getId()), DeviceAreaDO::getId, deviceAreaRequestVO.getId())
                .likeRight(StringUtils.isNotBlank(deviceAreaRequestVO.getAllPath()), DeviceAreaDO::getAllPath, deviceAreaRequestVO.getAllPath())
                .eq(StringUtils.isNotBlank(deviceAreaRequestVO.getCode()), DeviceAreaDO::getCode, deviceAreaRequestVO.getCode())
                .like(StringUtils.isNotBlank(deviceAreaRequestVO.getName()), DeviceAreaDO::getName, deviceAreaRequestVO.getName())
                .eq(deviceAreaRequestVO.getStatus() != null, DeviceAreaDO::getStatus, deviceAreaRequestVO.getStatus())
                .in(CollUtil.isNotEmpty(deviceAreaRequestVO.getStatusAll()), DeviceAreaDO::getStatus, deviceAreaRequestVO.getStatusAll());
        return deviceAreaDOQueryWrapper;
    }

    @Override
    public PageData<DeviceAreaResponseVO> selectPageList(DeviceAreaRequestVO deviceAreaRequestVO) {
        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = getDeviceAreaDOQueryWrapper(deviceAreaRequestVO);
        Page pageNum = Page.of(deviceAreaRequestVO.getPageNo() == null ? 1 : deviceAreaRequestVO.getPageNo(),
                deviceAreaRequestVO.getPageSize() == null ? 10 : deviceAreaRequestVO.getPageSize());
        Page<DeviceAreaDO> page = page(pageNum, deviceAreaDOQueryWrapper);
        PageParam pageParam = new PageParam(deviceAreaRequestVO.getPageNo(), deviceAreaRequestVO.getPageSize());
        PageData<DeviceAreaDO> pageData = PageUtils.getPageData(page, pageParam);
        Collection<DeviceAreaDO> data = pageData.getData();
        List<DeviceAreaResponseVO> deviceAreaResponseVOS =
                com.jtny.component.util.BeanUtils.copyToList(data, DeviceAreaResponseVO.class);
        if (CollUtil.isNotEmpty(data)) {
            getParentName(deviceAreaResponseVOS);
        }
        PageData<DeviceAreaResponseVO> deviceAreaResponseVOPageData = pageData.transformData(DeviceAreaResponseVO.class);
        deviceAreaResponseVOPageData.setData(deviceAreaResponseVOS);

        return deviceAreaResponseVOPageData;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByIds(List<Long> ids) {
        if (CollUtil.isNotEmpty(ids)) {
            checkParentNode(ids);
            this.removeBatchByIds(ids);
        }
    }

    void checkParentNode(List<Long> ids) {
        QueryWrapper<DeviceAreaDO> deviceTypeDOQueryWrapper = new QueryWrapper<>();
        deviceTypeDOQueryWrapper.lambda().in(CollUtil.isNotEmpty(ids), DeviceAreaDO::getParentId, ids);
        if (CollUtil.isNotEmpty(list(deviceTypeDOQueryWrapper))) {
            throw new ServiceException("存在下级节点！");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exportExcel(List<DeviceAreaExcelVO> deviceAreaExcelVOS) {
        if (CollUtil.isEmpty(deviceAreaExcelVOS)) {
            throw new ServiceException("导入数据不能为空！");
        }

        List<DeviceAreaRequestVO> deviceAreaRequestVOS = convertToRequestVO(deviceAreaExcelVOS);
        //excel重复
        Set<String> names = deviceAreaRequestVOS.stream().map(DeviceAreaRequestVO::getName).collect(Collectors.toSet());
        if (names.size() < deviceAreaRequestVOS.size()) {
            throw new ServiceException("名称重复！");
        }
        if (deviceAreaRequestVOS.stream().anyMatch(e -> e.getLevel() == 1)) {
            throw new ServiceException("一级区域请手动创建！");
        }

        //分页处理
        CopyOnWriteArrayList<DeviceAreaRequestVO> results = new CopyOnWriteArrayList<>();
        StringBuilder error = new StringBuilder();
        DealPageData.parallelDeal(deviceAreaRequestVOS, lists -> {
            lists.forEach(e -> {
                Map<String, Object> stringObjectMap = dealData(e);
                error.append(stringObjectMap.get("error"));
                results.addAll((List<DeviceAreaRequestVO>) stringObjectMap.get("list"));
            });
            return error;
        });

        if (StringUtils.isNotEmpty(error.toString())) {
            throw new ServiceException(error.toString());
        }

//        List<DeviceAreaDO> deviceAreaDOS = com.jtny.component.util.BeanUtils.copyToList(results, DeviceAreaDO.class);
//        saveBatch(deviceAreaDOS, 500);
//        //批量更新
//        deviceAreaDOS.forEach(e -> {
//            String path = StringUtils.isEmpty(e.getAllPath()) ? "0" : e.getAllPath();
//            e.setAllPath(path + "," + e.getId());
//        });
//        updateBatchById(deviceAreaDOS);

        List<DeviceAreaDO> deviceAreaDOS = new ArrayList<>();
        for (DeviceAreaRequestVO e : results) {
            DeviceAreaDO deviceAreaDO = new DeviceAreaDO();

            // 获取父节点的编码
            String parentCode = "";
            if (e.getLevel() > 1) {
                DeviceAreaRequestVO parentRequest = new DeviceAreaRequestVO();
                parentRequest.setName(e.getParentName());
                List<DeviceAreaResponseVO> parentResultVos = selectList(parentRequest);
                parentCode = CollUtil.isEmpty(parentResultVos) ? "" : parentResultVos.get(0).getCode();
            }

            // 生成新的唯一编码
            String newCode;
            if (StringUtils.isEmpty(parentCode)) {
                // 如果是一级节点，获取全局最大code并递增
                String maxCode = deviceAreaDAO.getMaxCodeForLevel(3);
                int maxCodeInt;
                if (StringUtils.isEmpty(maxCode)) {
                    maxCodeInt = 0;
                } else {
                    try {
                        maxCodeInt = Integer.parseInt(maxCode);
                    } catch (NumberFormatException ex) {
                        throw new ServiceException("最大编码转换失败: " + maxCode);
                    }
                }
                newCode = String.format("%03d", maxCodeInt + 1);
            } else {
                // 如果是子节点，获取父节点下的最大code并递增
                String maxCode = deviceAreaDAO.getMaxCodeForParent(parentCode);
                int maxCodeInt;
                if (StringUtils.isEmpty(maxCode)) {
                    maxCodeInt = 0;
                } else {
                    try {
                        maxCodeInt = Integer.parseInt(maxCode.substring(parentCode.length()));
                    } catch (NumberFormatException ex) {
                        throw new ServiceException("父节点下最大编码转换失败: " + maxCode);
                    }
                }
                newCode = parentCode + String.format("%03d", maxCodeInt + 1);
            }

            e.setCode(newCode);
            BeanUtils.copyProperties(e, deviceAreaDO);
            deviceAreaDOS.add(deviceAreaDO);
        }

        saveBatch(deviceAreaDOS, 500);
        //批量更新
        deviceAreaDOS.forEach(e -> {
            String path = StringUtils.isEmpty(e.getAllPath()) ? "0" : e.getAllPath();
            e.setAllPath(path + "," + e.getId());
        });
        updateBatchById(deviceAreaDOS);
    }


    private Map<String, Object> createResultMap(String error, List<DeviceAreaRequestVO> list) {
        Map<String, Object> map = new HashMap<>(2);
        map.put("error", error);
        map.put("list", list);
        return map;
    }

    public Map<String, Object> dealData(List<DeviceAreaRequestVO> list) {
        StringBuilder error = new StringBuilder();
        //父节点name
        Set<String> parentNames = list.stream()
                .map(DeviceAreaRequestVO::getParentName)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        if (parentNames.isEmpty()) {
            error.append("父名称不能为空;");
            return createResultMap(error.toString(), list);
        }

        QueryWrapper<DeviceAreaDO> deviceAreaDOQueryWrapper = new QueryWrapper<>();
        deviceAreaDOQueryWrapper.lambda().in(DeviceAreaDO::getName, parentNames);
        //按name分组
        Map<String, DeviceAreaDO> collectByCode =
                list(deviceAreaDOQueryWrapper).stream().collect(Collectors.toMap(DeviceAreaDO::getName, Function.identity()));

        for (DeviceAreaRequestVO deviceAreaRequestVO : list) {
            DeviceAreaDO deviceAreaDO = collectByCode.get(deviceAreaRequestVO.getParentName());
            if (Objects.isNull(deviceAreaDO)) {
                error.append("父名称:").append(deviceAreaRequestVO.getParentName()).append("有误;");
                continue;
            }
            deviceAreaRequestVO.setParentId(deviceAreaDO.getId())
                    .setAllPath(deviceAreaRequestVO.getAllPath())
                    .setStatus(DeviceAreaDO.ENABLE);
        }

        //重复
        String msg = checkNameDup(list, Boolean.TRUE);
        return createResultMap(error + msg, list);
    }

    private <T> void copyVO(List<T> source, List<DeviceAreaResponseVO> target) {
        source.forEach(e -> {
            DeviceAreaResponseVO deviceAreaResponseVO = new DeviceAreaResponseVO();
            BeanUtils.copyProperties(e, deviceAreaResponseVO);
            target.add(deviceAreaResponseVO);
        });
    }
}
